    /********************************************************/
    /*                                                      */
    /*                                                      */
    /*         Copyright (C), 1993  SEIKO EPSON Corp.       */
    /*         ALL RIGHTS RESERVED                          */
    /*                                                      */
    /*                                                      */
    /*  file name : strtod.c                                */
    /*                                                      */
    /*  Revision history                                    */
    /*      93/12/17    first release       S.Akaiwa        */
    /*      97/2/20	modified	    T.Katahira	    */
    /*			change function declaration	    */
    /*					to the ANSI style   */
    /*		97/3/27 change HUGE_VAL		M.Kudo	    */
    /*                                                      */
    /********************************************************/

#ifdef      UNIX
#include    "stdlib.h"
#include    "math.h"
#include    "float.h"
#include    "limits.h"
#include    "errno.h"
#include    "ctype.h"
#include    "smcvals.h"
#else
#include    <stdlib.h>
#include    <math.h>
#include    <float.h>
#include    <limits.h>
#include    <errno.h>
#include    <ctype.h>
#include    <smcvals.h>
#endif

     const double dPowTbl[] = {1E1, 1E2, 1E4, 1E8, 1E16, 1E32, 1E64, 1E128, 1E256};

double 
strtod(const char * sStrPtrP, char ** sEndPtrP)
   
    /********************************************************/
    /*                                                      */
    /*  The strtod function converts string pointed to by   */
    /*  sStrPtrP into double.                               */
    /*  The address where conversion ended is stored in     */
    /*  sEndPtrP.                                           */
    /*  The strtod function returns the converted value.    */
    /*  If nothing is converted, 0 is returned.             */
    /*  -HUGE_VAL or +HUGE_VAL is returned, and ERANGE is   */
    /*  stored in errno when overflow is generated.         */
    /*  0 is returned, and ERANGE is stored in errno when   */
    /*  underflow is generated.                             */
    /*                                                      */  
    /*  RETURN  0 :                                         */
    /*              (sStrPtrP == sEndPtrP) No conversion    */
    /*              (errno = ERANGE) Underflow              */
    /*              (other) Converted value                 */
    /*                                                      */
    /*          +/-HUGE_VAL :                               */
    /*              (errno = ERANGE) Overflow               */
    /*                                                      */
    /*          Others: Converted value                     */
    /*                                                      */
    /********************************************************/  
    /* char            * sStrPtrP  ;      pointer to string */ 
    /* char            **sEndPtrP  ;    convert end pointer */
    {
     char           *sStrTmpP;        /* tmp string pointer */
     char           *sStrNxtP;   /* tmp convert end pointer */
     int            iSign;                     /* sign flag */
     int            iExpSign;      /* sign flag in exponent */
     int            iDotFlg;              /* dot exist flag */
     int            iChgFlg;             /* conversion flag */
     int            iOvrFlg;        /* over/under-flow flag */
     int            iErrSav;             /* errno save area */
     int            iPowOfst;         /* power table offset */
     long           lExpDat;           /* exponential value */ 
     long           lExpTmp;         /* temporary exp value */
     double         dDat;                         /* result */
     DBL_LNG        _MIN_LMT;       /* double minimum value */

     _MIN_LMT.st._LH = 1;      /* initialize double minimum */
     _MIN_LMT.st._LL = 0;

     sStrTmpP = sStrPtrP;

     while(*sStrTmpP == ' '){                 /* skip space */
        sStrTmpP++;
     }

     if(*sStrTmpP == '-'){                      /* get sign */
        iSign = -1;
        sStrTmpP++;
     }
     else {
        if(*sStrTmpP == '+'){
            sStrTmpP++;
        }
        iSign = 1;                                          
     }
     
     dDat = 0.0;                            /* clear result */
     lExpTmp = 0;            /* clear tmp exponential value */
     lExpDat = 0;                /* clear exponential value */
     iDotFlg = 0;                   /* clear dot exist flag */
     iChgFlg = 0;                  /* clear conversion flag */
     iOvrFlg = 0;             /* clear over/under-flow flag */

     while(*sStrTmpP != '\n'){     /* convert mantissa part */
        if(isdigit(*sStrTmpP)){
           iChgFlg = 1;              /* set conversion flag */
           if(dDat <= DBL_MAX){
              dDat = ((dDat * 10.0) + (double)(*sStrTmpP - '0'));
           }
           else {
              lExpDat++;
           }
           if(iDotFlg == 1){
              lExpDat--;
           }
           sStrTmpP++;
        }
        else {
           if((iDotFlg == 0) && (*sStrTmpP == '.')){
              iDotFlg = 1;                     /* first dot */
              sStrTmpP++;
           }
           else {
              break;     /* expect number and dot character */
           }
        }
     }

     if(iChgFlg == 0){
        if(sStrTmpP != NULL){
           sStrTmpP = sStrPtrP;                            
        }
        return (double)0.0;                /* no conversion */
     }

                                /* convert exponential part */
     if((*sStrTmpP == 'e') || (*sStrTmpP == 'E')){
        iErrSav = errno;               /* save error number */  
        errno = 0;
        sStrTmpP++;
        
        lExpTmp = strtol(sStrTmpP, &sStrNxtP, 10);
                            /* convert string to long value */
        if(sStrTmpP != sStrNxtP){
           if(errno == ERANGE){
              if(lExpTmp < 0){
                 iOvrFlg = 1;                  /* underflow */
                 dDat = 0.0;
              }
              else {
                 iOvrFlg = 1;                   /* overflow */
                 dDat = HUGE_VAL;
              }
           }

           if(lExpTmp < 0){
              if(LONG_MIN - lExpTmp > lExpDat){
                 iOvrFlg = 1;                  /* underflow */
                 dDat = 0.0;
              }
              else {         /* calculate exponential value */
                 lExpDat = lExpDat + lExpTmp;   
              }
           }
           else {
              if(LONG_MAX - lExpTmp < lExpDat){
                 iOvrFlg = 1;
                 dDat = HUGE_VAL;               /* overflow */
              }
              else {         /* calculate exponential value */
                 lExpDat = lExpDat + lExpTmp;
              }
           }
           sStrTmpP = sStrNxtP;
        }
        else {
           sStrTmpP--;   /* exponential part does not exist */
        }

        errno = iErrSav;            /* restore error number */
     } 

     if(sEndPtrP != NULL){
        *sEndPtrP = sStrTmpP; /* set conversion end address */ 
     }

     if(dDat == 0.0){                      /* mantissa is 0 */
        return (double)dDat;
     }

     if(iOvrFlg == 0){
        if(lExpDat < 0){
           iExpSign = 1;
           lExpDat = -lExpDat;
        }
        else {
           iExpSign = 0;
        }
                                                            
        iPowOfst = 0;/* initial power table offset
                          (power data for exponent 1st bit) */

        while(lExpDat != 0){                 /* make result */
           if(lExpDat & 1 != 0){
              if(iExpSign == 1){
                 if((dDat >= 0) && (dDat < _MIN_LMT._D * dPowTbl[iPowOfst]) ||
                      (dDat < 0 && dDat > -_MIN_LMT._D * dPowTbl[iPowOfst])){
                    iOvrFlg = 1;               /* underflow */
                    dDat = 0.0;
                    break;
                 }
                                            /* renew result */
                 dDat = dDat / dPowTbl[iPowOfst];  
              }
              else {
                 if((dDat >= 0) && (dDat > DBL_MAX / dPowTbl[iPowOfst]) ||
                      (dDat < 0 && dDat < -DBL_MAX / dPowTbl[iPowOfst])){
                    iOvrFlg = 1;                /* overflow */
                    dDat = HUGE_VAL;
                    break;                                  
                 }
                                            /* renew result */
                 dDat = dDat * dPowTbl[iPowOfst];    
              }
           }
           iPowOfst++; /* renew power table offset for next 
                                               exponent bit */
           lExpDat = lExpDat >> 1;/* get bit for power data */ 
        }
     }

     if(iOvrFlg == 1){
        errno = ERANGE;
        if(dDat == 0.0){
           return (double)dDat;                /* underflow */
        }
        else {
           if(iSign == -1){
              return (double)-HUGE_VAL;       /* - overflow */
           }
           else {
              return (double)HUGE_VAL;        /* + overflow */
           }
        }
     }
                                  /* return converted value */
     return (double)(dDat * (double)iSign); 
    }

